package sbc.group1.lab1.mozartspaces;

import java.awt.Color;
import java.io.Serializable;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.RequestContext;
import org.mozartspaces.core.TransactionReference;
import org.mozartspaces.notifications.Notification;
import org.mozartspaces.notifications.NotificationListener;
import org.mozartspaces.notifications.NotificationManager;
import org.mozartspaces.notifications.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sbc.group1.lab1.common.Car;
import sbc.group1.lab1.common.Engine;
import sbc.group1.lab1.common.Order;
import sbc.group1.lab1.common.Part;
import sbc.group1.lab1.common.Engine.Type;
import sbc.group1.lab1.gui.AbstractFactoryPanel;

class FactoryPanel extends AbstractFactoryPanel {
    private static final Logger log = LoggerFactory.getLogger(FactoryPanel.class);
    
    private Space space;
    private final AtomicInteger nextId = new AtomicInteger();
    private NotificationManager notificationManager;
    private NotificationListener notificationListener = new NotificationListener() {
        @Override
        public void entryOperationFinished(Notification source,
                Operation operation, List<? extends Serializable> entries) {
            if (operation == Operation.WRITE) {
                for (Serializable serial : entries) {
                    if (serial instanceof Entry) {
                        
                        Object value;
                        //is always an Entry here, but depends on operation
                        if (serial instanceof Entry) {
                            value = ((Entry)serial).getValue();
                        }else {
                            value = serial;
                        }
                        log.debug("WRITE: " + source.getObservedContainer().getId()+ " - " + value.toString());
                        if (value instanceof Part) {
                            FactoryPanel.this.addPart((Part)value);
                        }else if (value instanceof Car) {
                            Car car = (Car)value;
                            if (source==notificationAssembled) {
                                addAssembledCar(car);
                            }else if (source==notificationCompleted) {
                                addCompletedCar(car);
                            }else{
                                addFinishedCar(car);
                            }
                        }
                    }
                }
            }else if (operation==Operation.DELETE || 
                    operation==Operation.TAKE) {
                    for (Serializable serial : entries) {
                        Object value;
                        //Is never an Entry here, but depends on operation
                        if (serial instanceof Entry) {
                            value = ((Entry)serial).getValue();
                        }else {
                            value = serial;
                        }
                        log.debug("DELETE/TAKE: " + source.getObservedContainer().getId() + " - "+ value.toString());
                        if (value instanceof Part) {
                            FactoryPanel.this.removePart((Part)value);
                        }else if (value instanceof Car) {
                            Car car = (Car)value;
                            if (source==notificationAssembled) {
                                removeAssembled(car);
                            }else if (source==notificationCompleted) {
                                removeCompleted(car);
                            }
                        }
                    }
                }
            }
        };

    private Notification notificationAssembled;

    private Notification notificationCompleted;
    public FactoryPanel(Space space) throws MzsCoreException, InterruptedException {
        this.space=space;
        notificationManager = new NotificationManager(space.getCore());
        notificationManager.createNotification(space.getCarBodyContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        notificationManager.createNotification(space.getDefectCarContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        notificationManager.createNotification(space.getEngineContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        notificationManager.createNotification(space.getFinishedCarContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        notificationManager.createNotification(space.getWheelContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        notificationAssembled=notificationManager.createNotification(space.getAssembledCarContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        notificationCompleted=notificationManager.createNotification(space.getCompletedCarContainer(), 
                notificationListener, Operation.ALL, 
                (TransactionReference)null, (RequestContext)null);
        
    }
    
    @Override
    protected void asyncProduceCarBodies(int quantity, double relativeDefects) {
        int id = nextId.incrementAndGet();
        Thread t = new Thread(new CarBodyProducer(id, quantity, space,relativeDefects));
        t.setDaemon(true);
        t.start();
    }

    @Override
    protected void asyncProduceEngines(int quantity, double relativeDefects, Engine.Type type) {
        int id = nextId.incrementAndGet();
        Thread t = new Thread(new EngineProducer(id, quantity, space, relativeDefects, type));
        t.setDaemon(true);
        t.start();
    }

    @Override
    protected void asyncProduceWheels(int quantity, double relativeDefects) {
        int id = nextId.incrementAndGet();
        Thread t = new Thread(new WheelProducer(id, quantity, space, relativeDefects));
        t.setDaemon(true);
        t.start();
    }

    @Override
    protected void onNewOrder(int numCars, Type engine, Color coatingColor) {
        try {
            space.addOrder(new Order(numCars,coatingColor,engine));
        } catch (MzsCoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
